import Flutter
import UIKit
import CocoaAsyncSocket

public class RebellionUdpPlugin: NSObject, FlutterPlugin {

    private var socket: GCDAsyncUdpSocket?
    private var multicastGroup: String?
    private var port: UInt16?
    private var channel: FlutterMethodChannel!

    public static func register(with registrar: FlutterPluginRegistrar) {
        let instance = RebellionUdpPlugin()
        instance.channel = FlutterMethodChannel(
            name: "rebellion_udp",
            binaryMessenger: registrar.messenger()
        )
        registrar.addMethodCallDelegate(instance, channel: instance.channel)
    }

    public func handle(
        _ call: FlutterMethodCall,
        result: @escaping FlutterResult
    ) {
        switch call.method {
        case "getPlatformVersion":
            result("iOS " + UIDevice.current.systemVersion)
            
        case "close":
            close()
        case "init":
            let myresult = call.arguments as? [String: Any]
            if let multicastAddress = myresult?["multicastAddress"] as? String,let port = myresult?["port"] as? Int{
                udpInit(multicastAddress: multicastAddress, port: UInt16(port))
            }
        case "isOpen":
            result(isOpen())
        case "send":
            if let data = call.arguments as? FlutterStandardTypedData {
                send(data: data)
            }
        case "sendToTarget":
            if let args = call.arguments as? [String: Any],
                let ip = args["ip"] as? String,
                let data = args["data"] as? FlutterStandardTypedData {
                sendTarget(ip: ip, data: data)
            }
        default:
            result(FlutterMethodNotImplemented)
        }
    }

    public func udpInit(multicastAddress: String, port: UInt16) {
        self.multicastGroup = multicastAddress
        self.port = port
        socket = GCDAsyncUdpSocket(delegate: self, delegateQueue: DispatchQueue.main)

        do {
            // 允许广播和多播
            try socket!.enableBroadcast(true)
            try socket!.enableReusePort(true)

            // 绑定端口
            try socket!.bind(toPort: port)
            try socket!.beginReceiving()

            // 加入多播组
            try socket!.joinMulticastGroup(multicastGroup!, onInterface: nil)

            print("UDP Multicast Client started on \(multicastAddress):\(port)")

        } catch {
            print("Socket setup failed: \(error)")
        }
    }

    public func send(data: FlutterStandardTypedData) {
        guard let socket = socket,let multicastGroup = multicastGroup, let port = port else { return }
        socket.send(data.data, toHost: multicastGroup, port: port, withTimeout: -1, tag: 0)
    }
    
    private func sendTarget(ip: String, data: FlutterStandardTypedData) {
        guard let socket = socket, let port = port else { return }
        socket.send(data.data, toHost: ip, port: port, withTimeout: -1, tag: 0)
    }
    
    public func isOpen() -> Bool {
        return socket != nil
    }
    
    public func close() {
        if socket != nil {
            socket!.close()
            socket = nil
        }
    }
}

extension RebellionUdpPlugin: GCDAsyncUdpSocketDelegate {
    public func udpSocket(_ sock: GCDAsyncUdpSocket, didReceive data: Data, fromAddress address: Data, withFilterContext filterContext: Any?) {
        guard let senderIP = GCDAsyncUdpSocket.host(fromAddress: address),
                let localIP = getLocalIPAddress(),
                senderIP != localIP else {
              // 忽略本机发出的消息
              return
          }
        self.channel.invokeMethod("read", arguments: FlutterStandardTypedData(bytes: data))
    }

    public func udpSocket(_ sock: GCDAsyncUdpSocket, didSendDataWithTag tag: Int) {
        print("📤 Message sent")
    }
    
    public func udpSocket(_ sock: GCDAsyncUdpSocket, didConnectToAddress address: Data) {
        self.channel.invokeMethod("write", arguments: nil)
    }

    public func udpSocketDidClose(_ sock: GCDAsyncUdpSocket, withError error: Error?) {
        print("❌ Socket closed: \(error?.localizedDescription ?? "no error")")
        self.channel.invokeMethod("closed", arguments: nil)
    }
    
    func getLocalIPAddress() -> String? {
        var address: String?
        var ifaddr: UnsafeMutablePointer<ifaddrs>?

        if getifaddrs(&ifaddr) == 0 {
            var ptr = ifaddr
            while ptr != nil {
                defer { ptr = ptr?.pointee.ifa_next }

                let interface = ptr!.pointee
                let addrFamily = interface.ifa_addr.pointee.sa_family

                if addrFamily == UInt8(AF_INET), // IPv4
                   let name = String(cString: interface.ifa_name, encoding: .utf8),
                   name == "en0" {

                    var addr = interface.ifa_addr.pointee
                    let buf = UnsafeMutablePointer<Int8>.allocate(capacity: Int(NI_MAXHOST))
                    defer { buf.deallocate() }

                    if getnameinfo(&addr, socklen_t(interface.ifa_addr.pointee.sa_len),
                                   buf, socklen_t(NI_MAXHOST),
                                   nil, 0, NI_NUMERICHOST) == 0 {
                        address = String(cString: buf)
                    }
                }
            }
            freeifaddrs(ifaddr)
        }

        return address
    }
}
